/* eslint-disable */
import * as zrender from "./zrender.js";
import { ComNode, Link, AnchorNode } from './NNode.js'

const NodeSizeConfig = {
    width: 65,
    height: 35,
    r: 3,
    workFlowR: 16,
}

const ArrowLine = zrender.Path.extend({
    type: 'ArrowLine',
    shape: {
        fromX: 0,
        fromY: 0,
        toX: 0,
        toY: 0
    },
    buildPath: function (path, shape) {
        let { fromX, fromY, toX, toY } = shape
        path.moveTo(fromX, fromY);
        let _width = Math.abs(fromX - toX)
        let _height = Math.abs(fromY - toY)
        if (_width > _height) {
            // 线条更长
        } else {
            // 线条更高
        }
        path.bezierCurveTo(Math.min(fromX, toX) + _width / 3, fromY, Math.min(fromX, toX) + _width / 3 * 2, toY, toX, toY);
        if (fromX < toX) {
            // 向右
            path.moveTo(toX - 5, toY - 5);
            path.lineTo(toX, toY);
            path.lineTo(toX - 5, toY + 5);
        }
        else {
            // 向左
            path.moveTo(toX + 5, toY - 5);
            path.lineTo(toX, toY);
            path.lineTo(toX + 5, toY + 5);
        }
    }
});

zrender.ArrowLine = ArrowLine

class AppManager {
    constructor(rootElement, width, height) {
        this.width = width
        this.height = height
        this.canvas = rootElement
        this.zr = null
        this.linkStatus = false
        this.sceneG = null
        this.canvasBoundingBox = null
        this.selectedElement = null
        this.selectedDragElement = null
        this.selectedFromElement = null
        this.selectedToElement = null
        this.selectedToAnchor = null
        this.dragLinkMode = null
        this.currentLink = null
        // 组件节点
        this.comNodes = []
        // 组件连线
        this.links = []

        this.callbacks = {}
        this.init()
        this.initEventListener()
        AppManager.instance = this
    }

    resize(width, height){
        this.zr.resize({width, height})
    }
    on(eventType, callback) {
        this.callbacks[eventType] = callback
    }

    trigger(eventType, data) {
        let fn = this.callbacks[eventType]
        if (fn) {
            fn(data)
        }
    }

    /**
     * 显示原有数据
     */
    showOriginData(flowNodes, flowLinks) {
        //添加画布节点
        flowNodes.forEach(node => {
            this.addNode0(node);
        })
        //添加画布线条
        flowLinks.forEach((link, index) => {
            this.addLink0(link);
        })
    }

    /**
     * 修改节点状态
     * @param {*} nodeData
     */
    changeNodeStatus(nodeData) {
        nodeData.isComp = nodeData.nodeGroup === 0
        let node = this.comNodes.find(item => {
            return item.b_data.resourceId === nodeData.resourceId
        })
        if(node){
            // node.changeStatusColor(nodeData.nodeState)
            if(node.b_data.nodeState != nodeData.alarmLevelCode){
                //nodeData.alarmLevelCode = 2：离线，1：在线
                if(nodeData.alarmLevelCode == 2){
                    node.changeNodeStatusImage(nodeData.levelIcon);
                }else if(nodeData.alarmLevelCode == 1){
                    node.removeNodeStatusImagesAndClearnTimer();
                }
                node.b_data.nodeState = nodeData.alarmLevelCode
            }
        }
    }

    /**
     * 更改线条颜色数据
     * @param {线条数据} linkData 
     */
    changeLinkStatus(linkData){
        let link = this.links.find(item => {
            return item.id == linkData.id;
        });
        if(link){
            if(link.linkState != linkData.state){
                //离线不通
                if(linkData.state == 2){
                    link.changeLinkStroke('#cc0b0b');
                    let linkDownImg = link.createLinkDownImg();
                    this.sceneG.add(linkDownImg);
                }else{
                    link.changeLinkStroke(linkData.linkConfig.linkColor);
                    //删除画布图片
                    this.sceneG.remove(link.linkDownImg);
                    //删除链路连线数据
                    link.removeLinkDownImg();
                }
                link.linkState = linkData.state;
            }
        }
    }

    /**
     * 开始连线
     */
    startLink() {
        this.linkStatus = true
    }

    /**
     * 删除连线
     */
    deleteLink(link) {
        let toIndex = link.fromNode.fromLinks.findIndex(item => {
            return item.id === link.id
        })
        link.fromNode.fromLinks.splice(toIndex, 1)
        let fromIndex = link.toNode.toLinks.findIndex(item => {
            return item.id === link.id
        })
        link.toNode.toLinks.splice(fromIndex, 1)
        this.sceneG.remove(link.el);
        this.sceneG.remove(link.textEl);
        this.sceneG.remove(link.linkDownImg);
        this.trigger('deleteNodeLink', link)
    }

    /**
     * 删除和目标节点ID相关联的所有线条
     * @param {目标节点ID} toNodeId 
     */
    deleteLink0(toNodeId){
        let linkIndexs = [];
        for (let index = 0; index < this.links.length; index++) {
            let item = this.links[index];
            if(item.toNode.b_data.resourceId == toNodeId){
                linkIndexs.push(index);
            };
        }
        console.log("删除目标资源ID==>"+toNodeId+"，当前节点相关的连线数据索引：",linkIndexs);
        if(linkIndexs && linkIndexs.length > 0){
            for (let linkIndex of linkIndexs) {
                let link = this.links[linkIndex];
                if(!link){
                    return;
                }
                this.links.splice(linkIndex, 1);
                this.sceneG.remove(link.el);
                this.sceneG.remove(link.textEl);
                this.sceneG.remove(link.linkDownImg);
            }
        }
    }


    /**
     * 删除节点
     * @param {*} node
     */
    deleteNode(node) {
        if(node.fromLinks.length === 0 && node.toLinks.length === 0){
            let nodeIndex = this.comNodes.findIndex(item => {
                return item.id === node.id
            })
            if(nodeIndex){
                this.comNodes.splice(nodeIndex, 1)
            }
            this.sceneG.remove(node.el)
            this.trigger('deleteNode', node)
        }else{
            let nodeIndex = this.comNodes.findIndex(item => {
                return item.id === node.id
            })
            if(nodeIndex){
                this.comNodes.splice(nodeIndex, 1)
            }
            this.sceneG.remove(node.el)
            this.trigger('deleteNode', node)
        }
    }

    /**
     * 删除拓扑节点
     * @param {资源ID} resId 
     */
    deleteNode0(resId){
        let nodeIndex = this.comNodes.findIndex(item => {
            return item.b_data.resourceId == resId;
        });
        console.log("删除目标资源ID==>"+resId+"，当前节点相关的数据索引：",nodeIndex);
        if(nodeIndex){
            let node = this.comNodes[nodeIndex];
            this.comNodes.splice(nodeIndex, 1);
            this.sceneG.remove(node.el);
        }
    }


    clickNode(){
        this.clickTimer = setTimeout(() => {
            if(this.selectedDragElement){
                this.trigger('clickNode', this.selectedDragElement.b_data)
            }
        }, 40);
    }

    cancelClickTimer(){
        if(this.clickTimer){
            clearTimeout(this.clickTimer)
        }
    }

    /**
     * 动态添加节点，根据节点数据生成画布节点。
     * @param {节点数据} node 
     */
    addNode0(node){
        let _pos = node.position
        let data = node
        // TODO 临时处理，需要注释
        data.isComp = node.nodeGroup === 0
        node = new ComNode(_pos.x, _pos.y, data)
        console.log("创建节点数据成功=====>", node);
        this.addNode('Component', node, false);
        return node;
    }

    /**
     * 动态添加节点，并计算节点坐标与偏移量
     * @param {动态添加节点} node 
     */
    addNode1(node){
        //根据圆点资源ID，查找圆点信息
        let center = this.comNodes.find(item => item.b_data.resourceId == node.centerResId);
        center.originalRadius = 200;
        center.x = center.b_data.position.x;
        center.y = center.b_data.position.y;
        //计算node节点坐标位置
        let position = this.layoutCircles(center);
        node.position = position;
        //添加给画布节点
        node = this.addNode0(node);
        //重置原始角度
        center.originalAngle = node.el.getBoundingRect().width;
    
    }

    /**
     * 通过中心圆点坐标，半径，便宜角度，计算圆圈上布局的圆点
     * @param {中心圆点} center 
     * @returns 
     */
    layoutCircles(center) {
        let radius = center.radius ? center.radius : center.originalRadius;
        let angle = center.angle ? center.angle : 0;
        let originalAngle = center.originalAngle ? center.originalAngle : 0;
        let offsetAngle = angle + originalAngle;
        if(offsetAngle > 360){
            offsetAngle = angle + 5;
            // radius = radius + center.originalRadius;
            // center.radius = radius;
        }
        //重置圆点便宜角度
        center.angle = offsetAngle;
        console.log("this center angle is", offsetAngle);
        return {
          x: center.x + radius * Math.cos(offsetAngle),
          y: center.y + radius * Math.sin(offsetAngle),
        };
      }

    /**
     * 添加显示节点
     * @param {*} renderNode
     */
    addNode(type, node, notify) {
        this.comNodes.push(node);
        this.sceneG.add(node.el)
        if(notify){
            this.trigger('addNode', node)
        }
    }

    /**
     * 根据传入的线条数据，创建连线
     * @param {线条数据} link 
     */
    addLink0(link){
        let vm = this
        let fromNode = null
        let toNode = null
        fromNode = vm.comNodes.find(nodeItem => {
            return nodeItem.b_data.resourceId === link.fromNodeId
        })
        toNode = vm.comNodes.find(nodeItem => {
            return nodeItem.b_data.resourceId === link.toNodeId
        })

        if(fromNode && toNode){
            let fromAnchorNode = new AnchorNode(fromNode, 0, 'output', {
                arcNodeId: link.fromPort,
                nodeId: link.fromNodeId
            })
            fromNode.addAnchorNode(fromAnchorNode)
            if(fromNode.b_data.nodeGroup != 1){
                let copyAnchorNode = new AnchorNode(fromNode, 0, 'input', {
                    arcNodeId: link.fromPort,
                    nodeId: link.fromNodeId
                })
                fromNode.addAnchorNode(copyAnchorNode)
            }



            let toAnchorNode = new AnchorNode(toNode, 0, 'input', {
                arcNodeId: link.toPort,
                nodeId: link.toNodeId
            })
            toNode.addAnchorNode(toAnchorNode)
            if(toNode.b_data.nodeGroup != 2){
                let copyAnchorNode = new AnchorNode(toNode, 0, 'output', {
                    arcNodeId: link.toPort,
                    nodeId: link.toNodeId
                })
                toNode.addAnchorNode(copyAnchorNode)
            }
            if(link.linkType=='2'){
                link.linkConfig.linkSize=5;
            }

            // todo 如果存在这个信息，表示这条线在产生告警
            if (link.lineStyleInfo){
                // lineStyle 连线样式，1：实线 ； 2：虚线
                let lineStyle = link.lineStyleInfo.lineStyle
                if (lineStyle == 1){
                  link.linkConfig.linkDashed = 0
                }else{
                  link.linkConfig.linkDashed = 1
                }
                // 连线颜色
                let linkColor = link.lineStyleInfo.linkColor
                link.linkConfig.linkColor = linkColor
                // 连线大小
                let linkSize = link.lineStyleInfo.linkSize
                link.linkConfig.linkSize = linkSize
            }
            //判断线条颜色
            if(link.state){
                //线条离线条件判断
                if(link.state == 2){
                    link.linkConfig.linkColor = '#cc0b0b'
                }
            }
            
            let linkNode = new Link(fromNode, fromAnchorNode, toNode, toAnchorNode, 'solid', link.linkName, link.linkType, link.id,link.linkConfig,link.state);
            console.log("创建连线数据成功====>", linkNode);
            this.addLink(linkNode)
        }
    }

    /**
     * 保存并新增连线
     * @param {需要新增的连线数据对象} link 
     */
    addLink(link) {
        this.links.push(link)
        this.sceneG.add(link.el)
        this.sceneG.add(link.textEl)
        if(link.linkState == 2){
            this.sceneG.add(link.linkDownImg);
        }
    }

    /**
     * 清空画布
     * 用于重置流程
     */
    clearCanvas() {
        this.links.forEach(link => {
            this.sceneG.remove(link.el)
            this.sceneG.remove(link.textEl)
        })
        this.comNodes.forEach(node => {
            this.sceneG.remove(node.el)
        })
        this.comNodes = []
        this.links = []
        this.sceneG.attr('position', [0, 0])
    }

    /**
     * 清空数据
     */
    clearData(){

    }

    initEventListener() {
        let { canvas, canvasBoundingBox, zr } = this
        let vm = this
        // 置入
        canvas.ondrop = (ev) => {
            ev.preventDefault();
            let msgData = JSON.parse(ev.dataTransfer.getData("TopoFlowChartData"));
            let { type, data } = msgData
            let _pos = this.getScenePosFromCanvasPos([ev.clientX - canvasBoundingBox.x, ev.clientY - canvasBoundingBox.y])
            let node = null
            node = new ComNode(_pos[0], _pos[1], data)
            this.trigger('beforeAddNode', node)
            // this.addNode(type, node, true)
        }
        // 拖动
        canvas.ondragover = (ev) => {
            ev.preventDefault();
        }

        zr.on('mousedown', (ev) => {
            if (vm.selectedDragElement) {
                vm.dragStartPos = { x: ev.offsetX, y: ev.offsetY }
                return
            }
            if (vm.selectedLink) {
                vm.selectedLink.toggleSelected()
                return
            }
            vm.trigger('clearSelect')
            this.trigger('clickNode', null)
        })

        zr.on('mousemove', (ev) => {
            this.cancelClickTimer()
            let selectedDragElement = vm.selectedDragElement
            let selectedFromElement = vm.selectedFromElement
            let scenePos = this.getScenePosFromCanvasPos([ev.offsetX, ev.offsetY])
            if (this.linkStatus) {
                // 连线中
                if (vm.dragLinkMode) {
                    // 移动连线
                    if (vm.currentLink === null) {
                        vm.currentLink = new zrender.ArrowLine({
                            shape: {
                                fromX: 0,
                                fromY: 0,
                                toX: 0,
                                toY: 0
                            },
                            style: {
                                fill: null,
                                stroke: 'red',
                                lineDash: [3, 3],
                                lineWidth: 2
                            }
                        });
                        vm.sceneG.add(vm.currentLink)
                    }

                    vm.currentLink.show()

                    let fx = selectedFromElement.x + NodeSizeConfig.width / 2
                    let fy = selectedFromElement.y + NodeSizeConfig.height / 2
                    vm.currentLink.attr('shape', {
                        fromX: fx,
                        fromY: fy,
                        toX: scenePos[0],
                        toY: scenePos[1]
                    })
                }
            } else {
                if (selectedDragElement) {
                    // 移动实体
                    selectedDragElement.dragMoveToCanvasPos([ev.offsetX, ev.offsetY])
                    selectedDragElement.refreshLinksFromMove()
                }
            }
        })

        zr.on('mouseup', (ev) => {
            let vm = this
            let scenePos = this.getScenePosFromCanvasPos([ev.offsetX, ev.offsetY])
            if (vm.linkStatus) {
                // 连线中，确定节点，取消连线
                vm.linkStatus = false
                vm.trigger('canelLink')
                vm.currentLink.hide()
                let targetElement = vm.comNodes.find(item => {
                    return item.getBoundingBox(20).contain(scenePos[0], scenePos[1])
                })
                if (targetElement) {
                    vm.selectedToElement = targetElement
                    vm.trigger('endLink', {
                        fromNode: vm.selectedFromElement,
                        toNode: vm.selectedToElement
                    })
                } else {
                    vm.dragStartPos = null
                    vm.linkStatus = false
                    this.selectedDragElement = null
                    this.dragLinkMode = null
                }
            } else {
                let currentNode = this.selectedDragElement
                vm.linkStatus = false
                vm.dragStartPos = null
                this.selectedDragElement = null
                this.dragLinkMode = null
                if(currentNode){
                    this.trigger('moveNode', currentNode)
                }
            }
        })
    }
    init() {
        let { canvas, width, height } = this
        canvas.oncontextmenu = function () {
            return false;
        };
        let zr = zrender.init(canvas, { width, height });
        let _rect = this.canvas.getBoundingClientRect()
        this.canvasBoundingBox = { x: _rect.x, y: _rect.y }

        this.zr = zr
        let sceneG = new zrender.Group()
        this.sceneG = sceneG
        let backGroundRect = new zrender.Rect({
            cursor: 'crosshair',
            draggable: true,
            shape: {
                x: -width*4,
                y: -height*4,
                width: 10000,
                height: 7000
            },
            style: {
                fill: 'transparent'
            }
        })

        backGroundRect.beforeUpdate = (ev) => {
            sceneG.attr('position', [backGroundRect.x, backGroundRect.y])
        }
        backGroundRect.on('mousewheel', (ev) => {
            if (ev.wheelDelta > 0) {
                this.canvasZoom('big')
            } else {
                this.canvasZoom('small')
            }
        })
        sceneG.add(backGroundRect)
        zr.add(sceneG)
    }

    drawLink(lineName, fromArcNodeId, toArcNodeId, fromNodeId, toNodeId, linkType, linkId) {
        let vm = this
        let fromNode = vm.selectedFromElement

        let fromAnchorNode = fromNode.anchors.find(item => {
            return item.type === 'output' && item.b_data.arcNodeId === fromArcNodeId && item.b_data.nodeId === fromNodeId
        })
        if(!fromAnchorNode){
            fromAnchorNode = new AnchorNode(fromNode, 0, 'output', {
                arcNodeId: fromArcNodeId,
                nodeId: fromNodeId
            })
            fromNode.addAnchorNode(fromAnchorNode)
            if(fromNode.b_data.nodeGroup != 1){
                let copyAnchorNode = new AnchorNode(fromNode, 0, 'input', {
                    arcNodeId: fromArcNodeId,
                    nodeId: fromNodeId
                })
                fromNode.addAnchorNode(copyAnchorNode)
            }
        }
        let toNode = vm.selectedToElement
        let toAnchorNode = toNode.anchors.find(item => {
            return item.type === 'input' && item.b_data.arcNodeId === toArcNodeId && item.b_data.nodeId === toNodeId
        })
        if(!toAnchorNode){
            toAnchorNode = new AnchorNode(toNode, 0, 'input', {
                arcNodeId: toArcNodeId,
                nodeId: toNodeId
            })
            toNode.addAnchorNode(toAnchorNode)
            if(toNode.b_data.nodeGroup != 2){
                let copyAnchorNode = new AnchorNode(toNode, 0, 'output', {
                    arcNodeId: toArcNodeId,
                    nodeId: toNodeId
                })
                toNode.addAnchorNode(copyAnchorNode)
            }
        }
        let link = new Link(fromNode, fromAnchorNode, toNode, toAnchorNode, 'solid', lineName, linkType, linkId)
        this.addLink(link);
    }

    canvasZoom(type) {
        let scale = this.sceneG.scaleX
        if (type === 'big') {
            this.sceneG.attr('scale', [scale + 0.1, scale + 0.1])
        } else {
            this.sceneG.attr('scale', [scale - 0.1, scale - 0.1])
        }
    }

    /**
     * 根据场景坐标找到最近的锚点和节点
     * @param {*} scenePos
     */
    getLatestAnchorAndNode(scenePos) {
        let item = null
        let anchor = null
        for (let i = 0; i < this.comNodes.length; i++) {
            let itemNode = this.comNodes[i]
            if (itemNode.id === this.selectedElement.id) {
                continue
            }
            let box = itemNode.getBoundingBox(20)
            if (box.contain(scenePos[0], scenePos[1])) {
                // 有包围
                item = itemNode
                if (scenePos[0] < (box.x + box.width / 2)) {
                    // 左侧 锚点
                    anchor = itemNode.anchors.find(_aItem => {
                        return _aItem.type === 'input'
                    })
                } else {
                    // 右侧 锚点
                    anchor = itemNode.anchors.find(_aItem => {
                        return _aItem.type === 'output'
                    })
                }
                break
            }
        }
        if (item === null) {
            return null
        } else {
            return {
                node: item,
                anchor: anchor
            }
        }
    }

    /**
     * 将画布坐标 转为 场景坐标
     * @param {*} clientPos
     * @returns
     */
    getScenePosFromCanvasPos(clientPos) {
        if (clientPos.length && clientPos.length > 0) {
            return this.sceneG.transformCoordToLocal(clientPos[0], clientPos[1])
        } else {
            return this.sceneG.transformCoordToLocal(clientPos.x, clientPos.y)
        }
    }

    /**
     * 将场景坐标 转为 画布坐标
     * @param {*} scenePos
     * @returns
     */
    getCanvasPosFromScenePos(scenePos) {
        if (scenePos.length && scenePos.length > 0) {
            return this.sceneG.transformCoordToGlobal(scenePos[0], scenePos[1])
        } else {
            return this.sceneG.transformCoordToGlobal(scenePos.x, scenePos.y)
        }
    }

    /**
     * 将节点组位置转为场景坐标
     * @param {*} groupPos
     * @returns
     */
    getScenePosFromGroupPos(groupPos) {
        if (groupPos.length && groupPos.length > 0) {
            return this.sceneG.transformCoordToGlobal(groupPos[0], groupPos[1])
        } else {
            return this.sceneG.transformCoordToGlobal(groupPos.x, groupPos.y)
        }
    }
}

export { AppManager }
